home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
Tools
/
glimpse-2.1
/
index
/
partition.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-16
|
21KB
|
637 lines
/* Copyright (c) 1994 Sun Wu, Udi Manber, Burra Gopal. All Rights Reserved. */
/* ./glimpse/index/partition.c */
#include "glimpse.h"
#include <sys/stat.h>
extern char INDEX_DIR[MAX_LINE_LEN];
extern int file_num; /* the number of files */
extern char *name_list[MAX_LIST]; /* to store the file names */
extern int *size_list; /* store size of each file */
extern int p_table[MAX_PARTITION]; /* partition table, the i-th partition begins at p_table[i] and ends at p_tables[i+1] */
extern int p_size_list[MAX_PARTITION]; /* sum of the sizes of the files in each partition */
extern int part_num; /* number of partitions, 1 initially since partition # 0 is not accessed */
extern int total_size; /* total size of the directory */
int part_size=DEFAULT_PART_SIZE; /* partition size */
int new_partition;
int files_per_partition;
int files_in_partition;
char patbuf[MAX_PAT];
extern unsigned char src_index_buf[REAL_INDEX_BUF];
extern unsigned char dest_index_buf[REAL_INDEX_BUF];
extern int memory_usage;
extern FILE *STATFILE;
extern FILE *MESSAGEFILE;
extern struct stat excstbuf;
extern struct stat incstbuf;
extern int OneFilePerBlock;
extern int ByteLevelIndex;
extern int StructuredIndex;
extern int attr_num;
extern char INDEX_DIR[MAX_LINE_LEN];
extern int AddToIndex;
extern int IndexableFile;
char *exin_argv[8];
int exin_argc;
char current_dir_buf[2*MAX_LINE_LEN + 4]; /* must have space to store pattern after directory name */
unsigned char dummypat[MAX_PAT];
int dummylen;
FILE *dummyout;
partition(dir_num, dir_name)
char **dir_name;
int dir_num;
{
int num_pat=0;
int num_inc=0;
int len;
int pat_len[MAX_EXCLUSIVE];
int inc_len[MAX_EXCLUSIVE];
CHAR *inc[MAX_INCLUSIVE]; /* store the patterns used to mask in files */
CHAR *pat[MAX_EXCLUSIVE]; /* store the patterns that are used to
mask out those files that are not to
be indexed */
int MinPartNum; /* minimum number of partitions */
int i=0, j;
int subtotal=0;
int pdx = 0; /* index pointer for p_table */
FILE *patfile; /* file descriptor for prohibit pattern file */
FILE *incfile; /* file descriptor for include pattern file */
char *current_dir; /* must have '\n' before directory name */
char s[MAX_LINE_LEN];
char working_dir[MAX_LINE_LEN];
struct stat sbuf;
current_dir_buf[0] = '\n';
current_dir_buf[1] = '\0';
current_dir = ¤t_dir_buf[1];
/* if (IndexableFile) goto directlytofsize; */
if ((dummyout = fopen("/dev/null", "w")) == NULL) return -1;
exin_argv[0] = "glimpseindex";
exin_argv[1] = "dummypat";
exin_argc = 2;
if ((dummylen = memagrep_init(exin_argc, exin_argv, MAX_PAT, dummypat)) <= 0) return -1; /* exclude/include pattern search */
sprintf(s, "%s/%s", INDEX_DIR, PROHIBIT_LIST);
patfile = fopen(s, "r");
if(patfile == NULL) {
/* fprintf(stderr, "can't open exclude-pattern file\n"); -- no need! */
num_pat = 0;
}
else {
while((num_pat < MAX_EXCLUSIVE) && fgets(patbuf, MAX_PAT, patfile)) {
if ((len = strlen(patbuf)) < 1) continue;
patbuf[len-1] = '\0';
if ((pat_len[num_pat] = convert2agrepregexp(patbuf, len-1)) == 0) continue;
pat[num_pat++] = (unsigned char *) strdup(patbuf);
}
fclose(patfile);
}
#if 0
printf("num_pat %d\n", num_pat);
for(i=0; i<num_pat; i++) printf("len=%d pat=%s\n", pat_len[i], pat[i]);
printf("memagrep=%d\n", memagrep_search(-pat_len[0], pat[0], 17, "\n.glimpse_index\nasdfk", 0, stdout));
#endif
sprintf(s, "%s/%s", INDEX_DIR, INCLUDE_LIST);
incfile = fopen(s, "r");
if(incfile == NULL) {
/* fprintf(stderr, "can't open include-pattern file\n"); -- no need! */
num_inc = 0;
}
else {
while((num_inc < MAX_INCLUSIVE) && fgets(patbuf, MAX_PAT, incfile)) {
if ((len = strlen(patbuf)) < 1) continue;
patbuf[len-1] = '\0';
if ((inc_len[num_inc] = convert2agrepregexp(patbuf, len-1)) == 0) continue;
inc[num_inc++] = (unsigned char *) strdup(patbuf);
}
fclose(incfile);
}
#if 0
printf("num_inc %d\n", num_inc);
for(i=0; i<num_inc; i++) printf("len=%d inc=%s\n", inc_len[i], inc[i]);
#endif
#ifdef SW_DEBUG
printf("dir_num = %d", dir_num-1);
#endif
directlytofsize:
if (dir_num <= 1) while (fgets(current_dir, MAX_LINE_LEN, stdin) == current_dir) {
current_dir[strlen(current_dir)-1] = '\0'; /* overwrite \n with \0 */
/* Get absolute path name of the directory or file being indexed */
if (-1 == stat(current_dir, &sbuf)) {
fprintf(stderr, "permission denied or non-existent: %s\n", current_dir);
continue;
}
if ((S_ISDIR(sbuf.st_mode)) && (current_dir[0] != '/')) {
getcwd(working_dir, MAX_LINE_LEN - 1);
if (-1 == chdir(current_dir)) {
fprintf(stderr, "Cannot chdir to %s\n", current_dir);
continue;
}
getcwd(current_dir, MAX_LINE_LEN - 1);
chdir(working_dir);
}
if (!IndexableFile) printf("Indexing \"%s\" ...\n", current_dir);
fsize(current_dir, pat, pat_len, num_pat, inc, inc_len, num_inc, 1); /* the file names will be in name_list[] */
}
else for(i=1; i<dir_num; i++) {
strcpy(current_dir, dir_name[i]);
/* Get absolute path name of the directory or file being indexed */
if (-1 == stat(current_dir, &sbuf)) {
fprintf(stderr, "permission denied or non-existent: %s\n", current_dir);
continue;
}
if ((S_ISDIR(sbuf.st_mode)) && (current_dir[0] != '/')) {
getcwd(working_dir, MAX_LINE_LEN - 1);
if (-1 == chdir(current_dir)) {
fprintf(stderr, "Cannot chdir to %s\n", current_dir);
continue;
}
getcwd(current_dir, MAX_LINE_LEN - 1);
chdir(working_dir);
}
if (!IndexableFile) printf("Indexing \"%s\" ...\n", current_dir);
fsize(current_dir, pat, pat_len, num_pat, inc, inc_len, num_inc, 1); /* the file names will be in name_list[] */
}
if (IndexableFile) return 0;
for(i=0; i<file_num; i++) total_size += size_list[i];
fprintf(STATFILE, "Size of files being indexed = %d B, Total #of files = %d\n", total_size, file_num);
printf("\nSize of files being indexed = %d B, Total #of files = %d\n", total_size, file_num); /* the only output the user sees */
#ifdef SW_DEBUG
for (i=0; i<file_num; i++)
printf("name_list[%d] = %s, size=%d\n", i, name_list[i], size_list[i]);
#endif /*SW_DEBUG*/
for (i=0; i<num_inc; i++) {
#if BG_DEBUG
memory_usage -= strlen(inc) + 2;
#endif /*BG_DEBUG*/
my_free(inc[i], 0);
}
for (i=0; i<num_pat; i++) {
#if BG_DEBUG
memory_usage -= strlen(pat) + 2;
#endif /*BG_DEBUG*/
my_free(pat[i], 0);
}
/* Life (algorithm) is much simpler, but encode/decode (I/O) is more complex: the p_table is irrelevant */
if (OneFilePerBlock)
return 0;
/* Now put the files into partitions */
i=0;
part_size = total_size / MaxNumPartition;
if (part_size <= 0) part_size = total_size;
size_list[file_num] = part_size;
if (file_num / 2 <= 1) {
p_table[0] = 0;
p_table[1] = 0;
p_table[2] = file_num;
part_num = 2;
return 0;
}
MinPartNum = (200 < file_num/2)? 200 : file_num/2;
while(part_num < MinPartNum) {
pdx = 0;
i = 0;
subtotal = 0;
while ((i < file_num) && (pdx < MAX_PARTITION)) {
if((pdx == 0) || (pdx == '\n')) {
/*
* So that there cannot be a partition #'\n' and a '\n' can indicate
* the end of the list of partition#s after the WORD_END_MARK.
* Also, partition#0 is not accessed so that sort does not
* ignore the partitions after partition# 0!
*/
p_table[pdx++] = i;
continue;
}
p_table[pdx++] = i;
while(subtotal < part_size) {
subtotal += size_list[i++];
}
#ifdef SW_DEBUG
printf("pdx=%d part_num=%d i=%d subtotal=%d\n", pdx, part_num, i, subtotal);
#endif
subtotal = 0;
}
part_num = pdx;
#if 0
printf("part_num = %d part_size = %d\n", part_num, part_size);
#endif
part_size = part_size * 0.9;
size_list[file_num] = part_size;
}
p_table[pdx] = file_num;
/* Calculate partition sizes for later output into statistics */
for (i=0; i<= part_num; i++)
for (j = p_table[i]; j<p_table[i+1]; j++)
p_size_list[i] += size_list[j];
return 0;
}
int printed_warning = 0;
/*
* Difference from above: does not build a new partition table:
* adds to the existing one (see glimpse.c, options -a and -f).
* -- added on dec 7th '93
*/
oldpartition(dir_num, dir_name)
char **dir_name;
int dir_num;
{
int num_pat=0;
int num_inc=0;
int len;
int pat_len[MAX_EXCLUSIVE];
int inc_len[MAX_EXCLUSIVE];
CHAR *inc[MAX_INCLUSIVE]; /* store the patterns used to mask in files */
CHAR *pat[MAX_EXCLUSIVE]; /* store the patterns that are used to
mask out those files that are not to
be indexed */
int i=0;
FILE *patfile; /* file descriptor for prohibit pattern file */
FILE *incfile; /* file descriptor for include pattern file */
char *current_dir; /* must have '\n' before directory name */
char s[MAX_LINE_LEN];
char working_dir[MAX_LINE_LEN];
struct stat sbuf;
current_dir_buf[0] = '\n';
current_dir_buf[1] = '\0';
current_dir = ¤t_dir_buf[1];
if ((dummyout = fopen("/dev/null", "w")) == NULL) return -1;
exin_argv[0] = "glimpseindex";
exin_argv[1] = "dummypat";
exin_argc = 2;
if ((dummylen = memagrep_init(exin_argc, exin_argv, MAX_PAT, dummypat)) <= 0) return -1; /* exclude/include pattern search */
sprintf(s, "%s/%s", INDEX_DIR, PROHIBIT_LIST);
patfile = fopen(s, "r");
if(patfile == NULL) {
/* fprintf(stderr, "can't open exclude-pattern file\n"); -- no need! */
num_pat = 0;
}
else {
stat(s, &excstbuf);
while((num_pat < MAX_EXCLUSIVE) && fgets(patbuf, MAX_PAT, patfile)) {
if ((len = strlen(patbuf)) < 1) continue;
patbuf[len-1] = '\0';
if ((pat_len[num_pat] = convert2agrepregexp(patbuf, len-1)) == 0) continue;
pat[num_pat++] = (unsigned char *) strdup(patbuf);
}
fclose(patfile);
}
#if 0
printf("num_pat %d\n", num_pat);
for(i=0; i<num_pat; i++) printf("len=%d pat=%s\n", pat_len[i], pat[i]);
#endif
sprintf(s, "%s/%s", INDEX_DIR, INCLUDE_LIST);
incfile = fopen(s, "r");
if(incfile == NULL) {
/* fprintf(stderr, "can't open include-pattern file\n"); -- no need! */
num_inc = 0;
}
else {
stat(s, &incstbuf);
while((num_inc < MAX_INCLUSIVE) && fgets(patbuf, MAX_PAT, incfile)) {
if ((len = strlen(patbuf)) < 1) continue;
patbuf[len-1] = '\0';
if ((inc_len[num_inc] = convert2agrepregexp(patbuf, len-1)) == 0) continue;
inc[num_inc++] = (unsigned char *) strdup(patbuf);
}
fclose(incfile);
}
#if 0
printf("num_inc %d\n", num_inc);
for(i=0; i<num_inc; i++) printf("len=%d inc=%s\n", inc_len[i], inc[i]);
#endif
#ifdef SW_DEBUG
printf("dir_num = %d part_num = %d", dir_num-1, part_num);
#endif
if (!OneFilePerBlock) { /* Worry about partitions */
files_per_partition = ((file_num - 1)/part_num) + 1; /* approximate only but gives a fair idea... */
files_in_partition = 0;
new_partition = part_num; /* part_num itself is guaranteed to be <= MaxNumPartition */
if (new_partition + 1 > MaxNumPartition) {
printed_warning = 1;
if (AddToIndex) {
fprintf(MESSAGEFILE, "Warning: partition-table overflow! Fresh indexing recommended.\n");
}
else {
fprintf(MESSAGEFILE, "Warning: partition-table overflow! Commencing fresh indexing...\n");
return partition(dir_num, dir_name);
}
}
}
if (dir_num <= 1) while (fgets(current_dir, MAX_LINE_LEN, stdin) == current_dir) {
current_dir[strlen(current_dir)-1] = '\0'; /* overwrite \n with \0 */
/* Get absolute path name of the directory or file being indexed */
if (-1 == stat(current_dir, &sbuf)) {
fprintf(stderr, "permission denied or non-existent: %s\n", current_dir);
continue;
}
if ((S_ISDIR(sbuf.st_mode)) && (current_dir[0] != '/')) {
getcwd(working_dir, MAX_LINE_LEN - 1);
if (-1 == chdir(current_dir)) {
fprintf(stderr, "Cannot chdir to %s\n", current_dir);
continue;
}
getcwd(current_dir, MAX_LINE_LEN - 1);
chdir(working_dir);
}
printf("Indexing \"%s\" ...\n", current_dir);
fsize(current_dir, pat, pat_len, num_pat, inc, inc_len, num_inc, 1); /* the file names will be in name_list[] */
}
else for(i=1; i<dir_num; i++) {
strcpy(current_dir, dir_name[i]);
/* Get absolute path name of the directory or file being indexed */
if (-1 == stat(current_dir, &sbuf)) {
fprintf(stderr, "permission denied or non-existent: %s\n", current_dir);
continue;
}
if ((S_ISDIR(sbuf.st_mode)) && (current_dir[0] != '/')) {
getcwd(working_dir, MAX_LINE_LEN - 1);
if (-1 == chdir(current_dir)) {
fprintf(stderr, "Cannot chdir to %s\n", current_dir);
continue;
}
getcwd(current_dir, MAX_LINE_LEN - 1);
chdir(working_dir);
}
printf("Indexing \"%s\" ...\n", current_dir);
if (-1 == fsize(current_dir, pat, pat_len, num_pat, inc, inc_len, num_inc, 1)) { /* the file names will be in name_list[] */
return -1;
}
}
if (!OneFilePerBlock) {
p_table[new_partition] = file_num;
part_num = new_partition;
}
for (i=0; i<num_inc; i++) {
#if BG_DEBUG
memory_usage -= strlen(inc) + 2;
#endif /*BG_DEBUG*/
my_free(inc[i], 0);
}
for (i=0; i<num_pat; i++) {
#if BG_DEBUG
memory_usage -= strlen(pat) + 2;
#endif /*BG_DEBUG*/
my_free(pat[i], 0);
}
for(i=0; i<file_num; i++) total_size += size_list[i];
fprintf(STATFILE, "Size of files being indexed = %d B, Total #of files = %d\n", total_size, file_num);
printf("\nSize of files being indexed = %d B, Total #of files = %d\n", total_size, file_num); /* the only output the user sees */
#ifdef SW_DEBUG
for (i=0; i<file_num; i++)
printf("name_list[%d] = %s, size=%d\n", i, name_list[i], size_list[i]);
#endif /*SW_DEBUG*/
return 0;
}
save_data_structures()
{
int i;
char s[MAX_LINE_LEN];
FILE *f_out;
FILE *p_out;
int j;
unsigned char c;
FILE *i_in;
FILE *i_out;
int offset, index, wordoffset;
char indexnumberbuf[256];
int onefileperblock, structuredindex;
/* Dump attributes */
if (StructuredIndex) {
int ret;
sprintf(s, "%s/%s", INDEX_DIR, ATTRIBUTE_FILE);
if (-1 == (ret = attr_dump_names(s))) {
fprintf(stderr, "can't open %s for writing\n", s);
exit(2);
}
}
/* Dump partition table; change index if necessary */
sprintf(s, "%s/%s", INDEX_DIR, P_TABLE);
if((p_out = fopen(s, "w")) == NULL) {
fprintf(stderr, "can't open for writing: %s\n", s);
exit(2);
}
if (!OneFilePerBlock) {
#ifdef SW_DEBUG
printf("part_num = %d, part_size = %d\n", part_num, part_size);
#endif
for(i=0; i<=part_num; i++) {
/* Assumes sizeof(int) is 32bits, which is true even for ALPHA */
putc((p_table[i] & 0xff000000) >> 24, p_out);
putc((p_table[i] & 0x00ff0000) >> 16, p_out);
putc((p_table[i] & 0x0000ff00) >> 8, p_out);
if (putc((p_table[i] & 0x000000ff), p_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
if (i==part_num) break;
if (p_table[i] == p_table[i+1]) {
fprintf(STATFILE, "part_num = %d, files = none, part_size = 0\n",i);
continue;
}
fprintf(STATFILE, "part_num = %d, files = %d .. %d, part_size = %d\n",
i, p_table[i], p_table[i+1] - 1, p_size_list[i]);
}
if (StructuredIndex) { /* check if we can reduce default 4B attributeids to smaller ones */
sprintf(s, "%s/.glimpse_split.%d", INDEX_DIR, getpid());
if((i_out = fopen(s, "w")) == NULL) {
fprintf(stderr, "can't open %s for writing\n", s);
exit(2);
}
sprintf(s, "%s/%s", INDEX_DIR, INDEX_FILE);
if((i_in = fopen(s, "r")) == NULL) {
fprintf(stderr, "can't open %s for reading\n", s);
exit(2);
}
/* modified the original in glimpse's main.c */
fgets(indexnumberbuf, 256, i_in);
fputs(indexnumberbuf, i_out);
fscanf(i_in, "%%%d\n", &onefileperblock);
fprintf(i_out, "%%%d\n", onefileperblock); /* If #of files change, then they are added to a new partition, which is updated above */
fscanf(i_in, "%%%d\n", &structuredindex);
if (structuredindex <= 0) structuredindex = 0;
fprintf(i_out, "%%%d\n", attr_num); /* attributes might have been added during last merge */
while(fgets(src_index_buf, REAL_INDEX_BUF, i_in)) {
if ((index=decode32b((src_index_buf[0] << 24)|(src_index_buf[1] << 16)|(src_index_buf[2] << 8)|(src_index_buf[3]))) > attr_num) continue;
if (attr_num < MaxNum8bPartition - 1) {
putc(encode8b(index), i_out);
if (fputs(src_index_buf + 4, i_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
}
else if (attr_num < MaxNum16bPartition - 1) {
index = encode16b(index);
putc((index & 0x0000ff00) >> 8, i_out);
putc(index & 0x000000ff, i_out);
if (fputs(src_index_buf + 4, i_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
}
else {
if (fputs(src_index_buf, i_out) == EOF) { /* no reduction */
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
}
}
fclose(i_in);
fflush(i_out);
fclose(i_out);
sprintf(s, "mv %s/.glimpse_split.%d %s/%s", INDEX_DIR, getpid(), INDEX_DIR, INDEX_FILE);
system(s);
}
}
else {
/* Don't care about individual file sizes in statistics since the user can look at it anyway by ls -l! */
sprintf(s, "%s/.glimpse_split.%d", INDEX_DIR, getpid());
if((i_out = fopen(s, "w")) == NULL) {
fprintf(stderr, "can't open %s for writing\n", s);
exit(2);
}
sprintf(s, "%s/%s", INDEX_DIR, INDEX_FILE);
if((i_in = fopen(s, "r")) == NULL) {
fprintf(stderr, "can't open %s for reading\n", s);
exit(2);
}
/* modified the original in glimpse's main.c */
fgets(indexnumberbuf, 256, i_in);
fputs(indexnumberbuf, i_out);
fscanf(i_in, "%%%d\n", &onefileperblock);
if (ByteLevelIndex) fprintf(i_out, "%%-%d\n", file_num); /* #of files might have changed due to -f/-a */
else fprintf(i_out, "%%%d\n", file_num); /* This was the stupidest thing of all! */
fscanf(i_in, "%%%d\n", &structuredindex);
if (structuredindex <= 0) structuredindex = 0;
fprintf(i_out, "%%%d\n", attr_num); /* attributes might have been added during last merge */
part_size = 0; /* current offset in the p_table file */
while(fgets(src_index_buf, REAL_INDEX_BUF, i_in)) {
if (StructuredIndex) {
if ((index=decode32b((src_index_buf[0] << 24)|(src_index_buf[1] << 16)|(src_index_buf[2] << 8)|(src_index_buf[3]))) > attr_num) continue;
if (attr_num < MaxNum8bPartition - 1) {
putc(encode8b(index), i_out);
}
else if (attr_num < MaxNum16bPartition - 1) {
index = encode16b(index);
putc((index & 0x0000ff00) >> 8, i_out);
putc(index & 0x000000ff, i_out);
}
else {
fwrite(src_index_buf, 1, 4, i_out); /* no reduction */
}
wordoffset = j = 4;
}
else wordoffset = j = 0;
while ((j < REAL_INDEX_BUF) && (src_index_buf[j] != WORD_END_MARK) && (src_index_buf[j] != ALL_INDEX_MARK) && (src_index_buf[j] != '\n')) j++;
if ((j >= REAL_INDEX_BUF) || (src_index_buf[j] == '\n')) continue;
/* else it is WORD_END_MARK or ALL_INDEX_MARK */
c = src_index_buf[j+1];
src_index_buf[j+1] = '\0';
fputs(src_index_buf+wordoffset, i_out);
src_index_buf[j+1] = c;
if (src_index_buf[j] == ALL_INDEX_MARK) {
fputc(DONT_CONFUSE_SORT, i_out);
if (fputc('\n', i_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
continue;
}
offset = encode32b(part_size);
fputc((offset & 0xff000000) >> 24, i_out); /* force big-endian */
fputc((offset & 0x00ff0000) >> 16, i_out);
fputc((offset & 0x0000ff00) >> 8, i_out);
fputc((offset & 0x000000ff), i_out);
if (fputc('\n', i_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
j++; /* @first byte of the block numbers, actually = c */
while(src_index_buf[j] != '\n') {
fputc(src_index_buf[j++], p_out);
part_size ++;
}
if (fputc('\n', p_out) == EOF) {
fprintf(stderr, "Error: write failed at %s:%d\n", __FILE__, __LINE__);
exit(2);
}
part_size ++;
}
fclose(i_in);
fflush(i_out);
fclose(i_out);
sprintf(s, "mv %s/.glimpse_split.%d %s/%s", INDEX_DIR, getpid(), INDEX_DIR, INDEX_FILE);
system(s);
}
fflush(p_out);
fclose(p_out);
/* Dump file names */
sprintf(s, "%s/%s", INDEX_DIR, NAME_LIST);
if((f_out = fopen(s, "w")) == NULL) {
fprintf(stderr, "can't open %s for writing\n", s);
exit(2);
}
for(i=0; i<file_num; i++) {
if (name_list[i] != NULL) fputs(name_list[i], f_out);
/* else empty line to indicate file that was removed = HOLE */
fputc('\n', f_out);
}
fflush(f_out);
fclose(f_out);
return 0;
}